{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "OulJtk4XKB3i"
   },
   "source": [
    "# ПРЕДОБРАБОТКА ДАННЫХ\n",
    "## Предсказание удельного электрического сопротивления (УЭС) пыли \n",
    "\n",
    "**Бизнес-постановка задачи** \n",
    "\n",
    "Для правильной разработки, проектирования, выбора типоразмера и изготовления установок электрогазоочистки, правильной пуско-наладки и эксплуатации электрофильтров (ЭФ) необходимо учитывать свойства улавливаемой пыли. А накопление большого слоя пыли вокруг электрочувствительных аппаратов может приводить к перебоям в их работе и аварийным ситуациям. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Постановка задачи анализа данных** \n",
    "\n",
    "Целью данной задачи является прогнозирование значения удельного электрического сопротивления пыли с помощью построения регрессионных моделей и их анализа. Зависимость УЭС пыли от температуры, влажности, дисперсного  и химического состава  пыли и газов  и от других параметров  используется в  расчете скорости дрейфа \n",
    "улавливаемых частиц.   Знание величины УЭС  пыли требуется  для  подготовки газов  перед электрофильтрами,   при определении  рациональных режимных параметров работы ЭФ для интенсификации процесса пылеулавливания.  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Обзор доступных данных**\n",
    "\n",
    "В выборке 360 наблюдений и 10 переменных. \n",
    "Таким образом, про каждое из 360 значений удельного электрического сопротивления мы знаем значения 9 его характеристик (температура, влажность, плотность пылевого слоя, размер частиц и т.д.)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## План анализа данных:\n",
    "\n",
    "  1. Загрузить данные\n",
    "  2. Обработать данные\n",
    "  3. Построить регрессионную модель (классический вариант)\n",
    "  4. Провести анализ полученной модели\n",
    "  5. Разделить данные для обучения и тестирования (продвинутый вариант)\n",
    "  6. Обучить модель на обучающей выборке\n",
    "  7. Провалидировать модель на тестовой выборке"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Загрузить данные"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Библиотека **warnings** отвечает за то, какие предупреждения (warnings) о работе будут выводиться пользователю. \n",
    "FutureWarning - предупреждения о том, как изменится работа библиотек в будущих версиях.\n",
    "Поэтому такие предупреждения мы будем игнорировать.\n",
    "Чтобы включить режим игнорирования мы отбираем все предупреждения из категории FutureWarning и выбираем для них действия 'ignore'.\n",
    "Это делается вызовом функции simplefilter c задание двух атрибутов: действия action и категории предупреждений category."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import warnings\n",
    "warnings.simplefilter(action='ignore')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Для корректной работы с данными в python требуется загрузить специальную библиотеку\n",
    "**pandas**, программную библиотеку на языке python для обработки и анализа данных."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd # загружаем библиотеку и для простоты обращения в коде называем её сокращенно pd"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Для корректной работы с графиками в python требуется загрузить специальную библиотеку\n",
    "**matplotlib**, программную библиотеку на языке python для визуализации данных двумерной и трехмерной графикой.\n",
    "\n",
    "Графики используются для облегчения интерпретации полученных результатов, а также в качестве иллюстраций в презентациях и отчетах. \n",
    "\n",
    "Оснвные методы для построения:\n",
    "* plot() - графики\n",
    "* semilogy() - график логарифмический\n",
    "* hist() - гистограммы"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt # загружаем библиотеку и для простоты обращения в коде называем её сокращенно plt\n",
    "# указываем, чтобы картинки отображались прямо в ноутбуке \n",
    "%matplotlib inline "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Для решения задачи мы будем использовать данные. Так как данные сохранены в формате xlsx (Excel), мы будем использовать специальную функцию\n",
    "из библиотеки pandas для загрузки таких данных **read_excel**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = pd.read_excel('dust.xlsx') # загружаем таблицу в переменную df"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Что важно посмотреть после того, как мы загрузили данные?*\n",
    "- проверить, что данные действительно загрузились\n",
    "- посмотреть на данные, чтобы удостовериться, что они правильные: колонки имеют те же названия, что и в таблице и т.д.\n",
    "\n",
    "Для того чтобы это сделать, нужно вызвать от переменной *df* метод **head()**, который выводит первые 5 строк таблицы. \n",
    "\n",
    "Для вызова метода объекта необходимо сначала написать *имя объекта*, затем поставить *точку*, затем уже написать *название метода*. Обратите внимание, что в конце обязательно ставить скобочки, потому что метод - это функция и в ней есть аргументы, просто в данном случае мы их не передаем, поэтому оставляем поле пустым"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Доступные признаки**\n",
    "\n",
    "Данные содержат два типа переменных:\n",
    "\n",
    "* Целевая: **resistivity**, УЭС пыли\n",
    "* Остальные переменные: 9 переменных, могут использоваться для прогноза целевой переменной.\n",
    "\n",
    "| Имя столбца        | Значение                                      | \n",
    "|:------------------:|:---------------------------------------------:|\n",
    "| temperature        | температура образца пыли                      | \n",
    "| humidity           | влажность   образца пыли                      |\n",
    "| density            | плотность образца                             |\n",
    "| dust_capacity      | емкость пыли                                  | \n",
    "| particle_size      | размер частиц в образце                       | \n",
    "| is_chelyabinsk     | образец был взят на месторождении в Челябинске|\n",
    "| conductivity       | электрическая проводимость                    |\n",
    "| dust_dispersiveness| дисперсность пыли                             |\n",
    "| formation          | способ образования пыли                       |\n",
    "| resistivity        | **Целевая переменная:** УЭС пыли              |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Посмотрим на размеры загруженной таблицы, у которой мы видели только первые 5 строк.\n",
    "\n",
    "Для этого вызываем поле **shape** у нашей переменной *df*. Поле вызывается также как метод, но в конце скобки не ставятся, так как для поля не предусмотрена передача аргументов. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df['resistivity'].value_counts()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Что означает первое и второе число?*\n",
    "\n",
    "Итак, таблица содержит 360 строк (объектов) и 10 столбцов (признаков), включая выходной (целевой) признак.\n",
    "Таблицу проверили, теперь можно приступать к обработке данных."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2. Обработать данные"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Проверяем данные на наличие пропусков и типов переменных**\n",
    "\n",
    "Начнем с проверки общей информации о данных.\n",
    "Для того чтобы это сделать, нужно обратиться вызвать у переменной *df* метод **info()**.\n",
    "\n",
    "Напомним, что в конце необходимо поставить скобочки."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df.info()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Анализируем результата выполнения команды:\n",
    "\n",
    "* 360 строк (entries)\n",
    "* 10 столбцов (Data columns)\n",
    "\n",
    "В данных присутствует три типа dtypes:\n",
    "* int64 - целое число  (1 столбец)\n",
    "* float64 - дробное число (6 столбцов)\n",
    "* object - текстовые или смешанные числовые и нечисловые значения (3 столбца)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Борьба с пропусками**\n",
    "\n",
    "Цифры в каждой строчке обозначают количество заполненных (*non-null*) значений. \n",
    "\n",
    "Так как эти цифры не в каждой строчке совпадают с числом строк (360), то в данных имеются пропуски: это переменные `is_chelyabinsk` и `dust_capacity`.\n",
    "\n",
    "С пропусками можно бороться несколькими способами:\n",
    "- удаление пропусков\n",
    "- заполнение пропусков"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "В переменной `is_chelyabinsk`, отвечающей за место получения образца пыли, порядка 30% пропущенных значений. Если предположить, что свойства пыли (химический состав) для различных образцов не отличаются, то мы можем избавиться от данной переменной.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df.drop(columns = ['is_chelyabinsk'], inplace = True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Убедимся, что такого столбца больше нет при помощи метода **columns**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df.columns"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Однако, удаление пропусков не всегда возможно, ведь это может привести к серьезной потере данных (как при удалении строк с отсутсвующими признаками, так и столбцов с пропусками в соответствующих признаках). В этом случае прибегают к заполнению пропусков. \n",
    "\n",
    "При заполнении используют простые или продвинутые методы. К простым можно отнести заполение некоторым константным значением или заполение средним/медианой.\n",
    "\n",
    "Переменная `dust_capacity` содержить значение емкости пыли - это количественная характеристика, заполним имеющиеся пропуски (12 шт.) средним значением."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mean = df.dust_capacity.mean()\n",
    "mean"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Теперь заполним пропуски в столбце `dust_capacity` средним значением емкости пыли."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df.dust_capacity.fillna(mean, inplace = True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Убедимся, что пропусков не осталось при помощи метода **isna**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df.dust_capacity.isna().sum()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df.info()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Визуализация**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Построим гистограммы данных.\n",
    "\n",
    "**В чем польза таких графиков при анализе данных?**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df.hist(figsize=(10, 10))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "И тепловую карту корреляции признаков."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import seaborn as sns\n",
    "sns.heatmap(df.corr(), cbar_kws= {'orientation': 'vertical'}, annot=True, vmin=-1, vmax=1, center= 0, cmap= 'coolwarm')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Обработка текстовых переменных**\n",
    "\n",
    "В наших данных 3 столбца  имеют значения типа object. В нашем случае, это текстовые признаки. Чтобы можно было подавать их на вход алгоритму или  модели, нам необходимо закодировать их.  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Мы рассмотрим два популярных метода кодирования категориальных признаков:\n",
    "*  метод **get_dummies()** из библиотеки **pandas** (One-Hot encode)\n",
    "*  метод **LabelEncoder()** из библиотеки **sklearn**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Для начала нам необходимо определить какие из признаков категориальные"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "text_features = ['conductivity', 'dust_dispersiveness', 'formation']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Далее нам необходимо преобразовать значения в выбранных колонках к числам, потому что алгоритмы и модели не умеют работать с текстом."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Первый способ заключается в том, чтобы заменить признаки типа: \n",
    "\n",
    "|                 |    Электрическая проводимость |  \n",
    "|-----------------|:-------------------------------:|\n",
    "|**Образец пыли 1**  |       average                 | \n",
    "|**Образец пыли 2**  |       good                    | \n",
    "|**Образец пыли 3**  |       low                     | \n",
    "\n",
    "Признаками типа:\n",
    "\n",
    "|                 |  Электрическая проводимость - average ? |  Электрическая проводимость - good ? |  Электрическая проводимость - low ? |\n",
    "|-----------------|:-----------------:|:---------------:|:--------------:|\n",
    "|**Образец пыли 1**  |       Да  (1)   |      Нет (0)  |      Нет (0) |\n",
    "|**Образец пыли 2**  |       Нет (0)   |      Да  (1)  |      Нет (0) |\n",
    "|**Образец пыли 3**  |       Нет (0)   |      Нет (0)  |      Да  (1) |\n",
    "\n",
    "Таким образом, наши новые признаки говорят относится ли oбразец пыли к определённому типу электрической проводимости или нет. При таком подходе для каждого категориального признака появляется столько новых колонок, сколько есть возможных категорий. Одна из колонок будет заполнена `1`, а остальные `0`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dummies = pd.get_dummies(data=df, columns=text_features)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "В функцию **get_dummies()** в качестве параметров передаём:\n",
    "*  **data** - исходные данные\n",
    "*  **columns** - имена колонок, в которых находятся категориальные признаки"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dummies.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Как видим, после кодирования у нас получилось на 5 признаков больше (14 вместо 9)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dummies.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Второй способ кодирования категориальных признаков заключается в том, чтобы просто присвоить каждой категории уникальный номер, и заменить значения признаков на соответствующие номера.\n",
    "\n",
    "Например, вместо таблицы:\n",
    "\n",
    "|                 |    Электрическая проводимость |  \n",
    "|-----------------|:-------------------------------:|\n",
    "|**Образец пыли 1**  |       average                   | \n",
    "|**Образец пыли 2**  |       average                     |\n",
    "|**Образец пыли 3**  |       good                     |\n",
    "|**Образец пыли 4**  |       low                   |\n",
    "|**Образец пыли 5**  |       average                      |\n",
    "|**Образец пыли 6**  |       good                     | \n",
    "\n",
    "Получаем:\n",
    "\n",
    "|                 |    Электрическая проводимость |  \n",
    "|-----------------|:-------------------------------:|\n",
    "|**Образец пыли 1**  |       1                       | \n",
    "|**Образец пыли 2**  |       1                       | \n",
    "|**Образец пыли 3**  |       2                       |\n",
    "|**Образец пыли 4**  |       3                       |\n",
    "|**Образец пыли 5**  |       1                       |\n",
    "|**Образец пыли 6**  |       2                       |\n",
    "\n",
    "Здесь все значения **average** заменены на `1`, **good** заменены на `2`, **low** заменены на `3`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.preprocessing import LabelEncoder"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Сперва создаем прототип кодировщика: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "label_encoder = LabelEncoder()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Так как нам нужно закодировать сразу список признаков, мы будем делать это в цикле. Рассматриваем каждый текстовый признак из списка *text_features*, далее методу **fit_transform()** передаем в качестве аргумента этот признак\n",
    "\n",
    "\n",
    "К полученным числовым представлениям признака будем прибавлять единичку, чтобы кодирование начиналось с 1, а не с 0. Затем результат будем записывать в табличку. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for col in text_features:\n",
    "    df[col] = label_encoder.fit_transform(df[col]) + 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Посмотрим на данные теперь:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "При таком способе кодирования количество признаков не меняется."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Чем может быть обусловлен выбор метода кодирования признаков?**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Построить регрессионную модель (классический вариант)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Используя средства библиотеки **statsmodels** построим модель множественной линейной регрессии УЭС на остальные предикторы и проанализируем полученный результат."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import statsmodels.api as sm\n",
    "import statsmodels.formula.api as smf\n",
    "\n",
    "results = smf.ols('resistivity ~ temperature + humidity + density + dust_capacity + particle_size + conductivity + dust_dispersiveness + formation', data=df).fit()\n",
    "results.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Препарируем OLS model\n",
    "\n",
    "\n",
    "`model = sm.ols(formula=\"a ~ b + c\", data=df).fit()`\n",
    "\n",
    "`result.summary()`\n",
    "\n",
    "В левой части первой таблицы представлена основная информация об обучаемой модели:\n",
    "\n",
    "| Элемент                    | Описание                                                                          |\n",
    "|:--------------------------:|:---------------------------------------------------------------------------------:|\n",
    "| Dep. Variable              | Какая переменная является ответом в модели                                        |\n",
    "| Model                      | Какую модель вы используете                                                       | \n",
    "| Method                     | Как рассчитывались параметры модели                                               | \n",
    "| No. Observations           | Количество наблюдений                                                             | \n",
    "| DF Residuals               | Степени свободы остатков = количество наблюдений - количество параметров модели   |\n",
    "| DF Model                   | Количество параметров в модели (не включая постоянный член, если он присутствует) |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Правая часть первой таблицы показывает качество обученной модели:\n",
    "\n",
    "| Элемент                    | Описание                                                                          |\n",
    "|:--------------------------:|:---------------------------------------------------------------------------------:|\n",
    "| R-squared              | Коэффициент детерминации. Статистическая мера того, насколько хорошо линия регрессии аппроксимирует реальные точки данных.|\n",
    "| Adj. R-squared                     | Приведенное выше значение скорректированное на основе количества наблюдений и степеней свободы остатков.| \n",
    "| F-statistic                    | Мера, насколько значима полученная модель. Среднеквадратическая ошибка модели, деленная на среднеквадратичную ошибку остатков| \n",
    "| Prob (F-statistic)           | Вероятность того, что вы получите приведенную выше статистику или еще более эксремальную, учитывая нулевую гипотезу о том, что модель незначима (p-value)| \n",
    "| Log-likelihood               | Значение логарифма функции правдоподобия |\n",
    "| AIC                   | Информационный критерий Акаике. Корректирует логарифмическую вероятность на основе количества наблюдений и сложности модели. |\n",
    "| BIC                   | Байесовский информационный критерий. Аналогичен AIC, но имеет более высокий штраф для моделей с большим количеством параметров.|"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Вторая таблица представляет информацию по каждому из коэффициентов\n",
    "\n",
    "| Элемент                    | Описание                                                                          |\n",
    "|:--------------------------:|:---------------------------------------------------------------------------------:|\n",
    "|             | Название переменной в модели|\n",
    "| coef | Расчетное значение коэффициента| \n",
    "| std err| Средняя квадратическая ошибка оценки коэффициента| \n",
    "|t| Значение t-статистики. Это показатель того, насколько статистически значим коэффициент.| \n",
    "|$$ P > |t|$$| P-value для проверки нулевой гипотезы о том, что коэффициент = 0. Если меньше уровня значимости, это указывает на наличие статистически значимой связи между переменной и ответом|\n",
    "| 95.0% Conf. Interval| Нижнее и верхнее значения 95% доверительного интервала для коэффициента |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "В последней части таблицы приводятся несколько статистических тестов для оценки распределения остатков.\n",
    "\n",
    "| Элемент                    | Описание                                                                          |\n",
    "|:--------------------------:|:---------------------------------------------------------------------------------:|\n",
    "|Skewness| Мера симметрии данных относительно среднего. Нормально распределенные ошибки должны быть симметрично распределены относительно среднего (равные суммы выше и ниже линии)|\n",
    "|Kurtosis| Мера формы распределения. Сравнивает количество данных, близких к среднему, с данными, далекими от среднего (в хвостах)| \n",
    "|Omnibus| Тест Д’Агостино. Он обеспечивает комбинированный статистический тест на отличие асимметрии и эксцесса от нормального| \n",
    "|Prob(Omnibus)| P-value для теста Д’Агостино, нулевая гипотеза - остатки распределены нормально| \n",
    "|Jarque-Bera| Тест Харке—Бера на нормальность по значениям асимметрии и эксцесса|\n",
    "|Prob (JB)| P-value для теста Харке—Бера, нулевая гипотеза - остатки распределены нормально |\n",
    "|Durbin-Watson| Тест на наличие автокорреляции (ошибки не являются независимыми). В случае отсутствия автокорреляции значение DW=2. При положительной автокорреляции DW стремится к 0, а при отрицательной — к 4:|\n",
    "|Cond. No| Тест на мультиколлинеарность. Если превышает 30, регрессия может иметь сильную мультиколлинеарность. |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Как по $p-value$ определить, есть ли основания отвергнуть нулевую гипотезу. \n",
    "\n",
    "1. Зафиксируем уровень значимости $\\alpha$ – это вероятность отвергнуть нулевую гипотезу при условии, что она верна.\n",
    "2. $p-value$ – это минимальный уровень значимости, на котором нулевая гипотеза может быть отвергнута.\n",
    "\n",
    "Значит, если:\n",
    "\n",
    "$p−value < \\alpha$ $\\Rightarrow$ $H_0$ отвергаем на уровне значимости $\\alpha$ (на имеющихся данных)\n",
    "\n",
    "$p−value \\geq \\alpha$ $\\Rightarrow$ $H_0$ не отвергаем на уровне значимости $\\alpha$ (на имеющихся данных)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. Провести анализ полученной модели\n",
    "**АНАЛИЗИРУЕМ ЭТО ;)**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Немного магии или алхимии анализа данных**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Вспомним, как выглядит распределение целевой переменной."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df.resistivity.hist(figsize=(5, 5))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Давайте преобразуем нашу целевую переменную прологарифмировав значения. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "df['lnR']=np.log(df['resistivity'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df.lnR.hist(figsize=(5, 5))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Построим новую модель, теперь уже для преобразованного значения целевой переменной."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "results_2 = smf.ols('lnR ~ temperature + humidity + density + dust_capacity + particle_size + conductivity + dust_dispersiveness + formation', data=df).fit()\n",
    "results_2.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Стало ли лучше?**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Тогда давайте добавим еще несколько признаков, используя исходные столбцы."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df['temperature2']=df['temperature']**2\n",
    "df['humidityIn']=1/df['humidity']\n",
    "df['Lndensity']=np.log(df['density'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Посмотрим на таблицу корреляций."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sns.heatmap(df.corr(), cbar_kws= {'orientation': 'vertical'}, annot=True, fmt='.1f', vmin=-1, vmax=1, center= 0, cmap= 'coolwarm')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "И построим обновленную модель."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "results_3 = smf.ols('lnR ~ temperature2 + temperature + humidity + humidityIn + density + Lndensity + dust_capacity + particle_size + conductivity + dust_dispersiveness + formation', data=df).fit()\n",
    "results_3.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Анализируйя предыдущий результат удалим признаки, коэффициенты перед которыми статистически незначимы."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "results_4 = smf.ols('lnR ~ temperature + humidity + humidityIn + density + Lndensity + conductivity + formation', data=df).fit()\n",
    "results_4.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Итоговая формула модели выглядит так\n",
    "\n",
    "$$lnR = -0.0428\\cdot temperature -0.1909\\cdot humidity + 15.7884\\cdot humidityIn -0.0146\\cdot density+ 14.0062\\cdot Lndensity + 1.2576\\cdot conductivity -1.4192\\cdot formation -79.2692$$\n",
    "\n",
    "А если вспомнить, что задача стояла в предсказаний значения `resistivity`, необходимо провести обратное преобразование, чтобы перейти от `lnR` к `resistivity`.\n",
    "\n",
    "$$resistivity = e^{-0.0428\\cdot temperature -0.1909\\cdot humidity + 15.7884\\cdot humidityIn -0.0146\\cdot density+ 14.0062\\cdot Lndensity + 1.2576\\cdot conductivity -1.4192\\cdot formation -79.2692}$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**А какие еще изменения модели (или предобработки данных) можно протестировать, чтобы улучшить качество предсказаний?**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Проверка допущений применения классической модели линейной регрессии"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###### Допущение 1. Ошибка имеет центрированное распределение"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Построим $qq$-график и гистограмму ошибок(остатков модели) для визуальной проверки."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import scipy as sc\n",
    "plt.figure(figsize=(16,7))\n",
    "plt.subplot(121)\n",
    "sc.stats.probplot(results_4.resid, dist=\"norm\", plot=plt)\n",
    "plt.subplot(122)\n",
    "np.log(results_4.resid).plot.hist()\n",
    "plt.xlabel('Остатки модели', fontsize=14)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Проверим гипотезу о том, что среднее значение ошибок $\\mu = 0$.\n",
    "\n",
    "**$t$-тест Стьюдента** \n",
    "\n",
    "$H_0$:$E(X)=\\mu$.\n",
    "\n",
    "$H_1$:$H_0$ - неверна."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from scipy import stats\n",
    "print(\"Критерий Стьюдента: p-value=%f\" % stats.ttest_1samp(results_4.resid, 0)[1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "results_4.resid.mean()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Среднее значение равно $\\mu = 0$, однако по $qq$-графику и гистограмме видно, что данные явно не являются нормально распределенными. \n",
    "\n",
    "Проверим это статситическим критерием.\n",
    "\n",
    "**Тест Лиллиефорса** \n",
    "\n",
    "$H_0$:распределение нормальное.\n",
    "\n",
    "$H_1$:распределение не нормальное."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from statsmodels.stats.diagnostic import lilliefors\n",
    "print(\"Критерий Лиллиефорса, p-value: %f\" % lilliefors(results_4.resid, dist='norm')[1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Первое допущение нарушается.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###### Допущение 2. Гомоскедастичность регрессионных остатков и отсутствие автокорреляции."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Тест Бройша — Пагана**  — один из статистических тестов для проверки наличия гетероскедастичности случайных ошибок регрессионной модели. В данном тесте проверяется линейная зависимость дисперсии случайных ошибок от некоторого набора переменных. Если статистика теста имеет значение $p-value$ ниже соответствующего уровня значимости, то нулевая гипотеза о гомоскедастичноости отвергается."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import statsmodels.stats.api as sms\n",
    "print('Тест Бройша — Пагана:  p-value=%f' % sms.het_breuschpagan(results_4.resid, results_4.model.exog)[1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Критерий Дарбина—Уотсона** — статистический критерий, используемый для тестирования автокорреляции первого порядка элементов исследуемой последовательности. В случае отсутствия автокорреляции статистика критерия равна 2, при положительной автокорреляции статистика стремится к нулю, а при отрицательной — к 4."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from statsmodels.stats.stattools import durbin_watson\n",
    "durbin_watson(results_4.resid)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.scatter(results_4.predict(),  results_4.resid,\n",
    "            c='blue', marker='o', label='Training data')\n",
    "plt.xlabel('Предсказания модели')\n",
    "plt.ylabel('Остатки')\n",
    "plt.hlines(y=0, xmin=-6, xmax=15,  color='red')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Второе допущение нарушается.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###### Допущение 3. Предикторы не должны быть связаны функциональной линейной зависимостью."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sns.heatmap(df[['temperature', 'humidity','humidityIn','density', 'Lndensity', 'conductivity', 'formation']].corr(), cbar_kws= {'orientation': 'vertical'}, annot=True, fmt='.1f', vmin=-1, vmax=1, center= 0, cmap= 'coolwarm')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Функциональная линейная зависимость наблюдается у переменнных `density` и `Lndensity` - значит из этих двух переменных необходимо оставить только одну(ту, которая сильнее связана с откликом). Также видна сильная линейная зависимость между переменными `humidity` и `humidityIn`.\n",
    "\n",
    "**Третье допущение нарушается.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###### Допущение 4. Объем выборки должен быть значительно больше числа признаков."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Выборка $m=360 \\gg n=7$\n",
    "\n",
    "**Четвертое допущение выполняется.**\n",
    "\n",
    "**Ну хоть что-то ;)**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Вывод:** при нарушении допущений классической линейной регрессии мы все еще можем воспользоваться моделью для прогнозирования, однако необходимо учитывать следующее:\n",
    "   - доверительные интервалы, полученные для коэффициентов модели, могут быть ненадежными;\n",
    "   - при построении модели были учтены не все зависимости в данных, поэтому модель можно улучшить.\n",
    "   \n",
    "Коэффициент детерминации  $R^2$ итоговой модели равен $0.707$."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Получим предсказание нашей модели для произвольного образца пыли (придумаем его сами)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df_predict = pd.DataFrame({'temperature': [20],\n",
    "                           'humidity': [1.4],\n",
    "                           'humidityIn': [0.71],\n",
    "                           'density': [1158],\n",
    "                           'Lndensity': [7.05],\n",
    "                           'conductivity': [1],\n",
    "                           'formation': [2]})\n",
    "df_predict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "results_4.predict(df_predict)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.exp(results_4.predict(df_predict))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Машинное обучение (начало)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. Разделить данные для обучения и тестирования"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Разделение исходных данных на обучающий и тестовый набор используется для оценки производительности алгоритмов машинного обучения.\n",
    "\n",
    "Набор обучающих данных используется для обучения модели, все ответы целевой переменной для этого набора нам известны. Второй набор называется набором тестовых данных, этот набор используется исключительно для прогнозов.\n",
    "\n",
    "Библиотека `scikit-learn` предоставляет нам модуль `model_selection`, в котором у нас есть функция разделения `train_test_split()`.\n",
    "\n",
    "\n",
    "```python\n",
    "train_test_split(*arrays, test_size=None, train_size=None, random_state=None, shuffle=True, stratify=None)\n",
    "```\n",
    "\n",
    "Параметры:\n",
    "\n",
    "- arrays: входные данные, такие как списки, массивы, фреймы данных или матрицы.\n",
    "- test_size: это вещественное число, значение которого находится в диапазоне от 0.0 до 1.0, оно представляет собой долю нашего размера тестовых данных.\n",
    "- train_size: то вещественное число, значение которого находится в диапазоне от 0.0 до 1.0, оно представляет собой долю нашего размера обучающих данных.\n",
    "- random_state: этот параметр используется для управления перемешиванием, применяемой к данным перед применением разделения.\n",
    "- shuffle: этот параметр используется для перетасовки данных перед разделением, его значение по умолчанию равно true.\n",
    "- stratify: этот параметр используется для разделения данных стратифицированным образом."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn import model_selection"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dataset_train, dataset_test = model_selection.train_test_split(df, test_size=0.3, random_state=42)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dataset_train"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dataset_test"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train, X_test, y_train, y_test = model_selection.train_test_split(df.drop(columns = ['resistivity', 'lnR']), df['lnR'], \n",
    "                                                    test_size=0.3, \n",
    "                                                    random_state=42)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_test"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "y_train"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "y_test"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Выбираем метод, который будем использовать**\n",
    "\n",
    "Проще всего начать с простых методов. \n",
    "* Линейная регрессия *linear regression*\n",
    "\n",
    "На выбор метода для построения модели влияет набор признаков, размер выборки, интуиция про то, какая связь между входными переменными и целевой. Но часто решение принимается исходя из того, какая модель сработала лучше."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Для корректной работы с методами построения моделей в python требуется загрузить специальную библиотеку\n",
    "**sklearn**, программную библиотеку на языке python для для машинного обучения и анализа данных.\n",
    "\n",
    "Мы импортируем модул для линейных моделей из этой библиотеки:\n",
    " * *linear_model* - тут находятся все линейные модели\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn import linear_model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Прежде чем начать делать ремонт, нужно подготовить инструменты для работы. Аналогично в нашем случае, прежде чем обучать модели, нужно создать их прототипы.  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Чтобы создать модель линейной регресии, пишем имя модуля 'linear_model', затем точку, затем название модели.\n",
    "\n",
    "Для этого нужно выполнить следующий код:\n",
    "\n",
    "```python\n",
    "linear_regression_model = linear_model.LinearRegression()\n",
    "linear_regression_model\n",
    "```\n",
    "\n",
    "Результат выполнения должен быть следующим:\n",
    "\n",
    "```python\n",
    "LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)\n",
    "```\n",
    " или\n",
    " \n",
    "```python\n",
    "LinearRegression()\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "linear_regression_model = linear_model.LinearRegression() # создаем модель"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "linear_regression_model # смотрим, что получилось"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. Обучить модель на обучающей выборке\n",
    "\n",
    "Теперь, когда мы создали прототипы обеих моделей, можем их обучить с помощью обучающей выборки. \n",
    "\n",
    "Для этого вызываем метод **fit()** у каждой модели и передаем ему на вход два аргумента: \n",
    "таблицу входных признаков и столбец значений целевой переменной - (X_train, y_train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "linear_regression_model.fit(X_train, y_train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train.columns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Выведем параметры модели в явном виде\n",
    "print(linear_regression_model.intercept_, linear_regression_model.coef_)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* Мы получили обученную модель. \n",
    "* Теперь необходимо провалидировать модель на новых(которых модель не видела) тестовых данных. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7. Провалидировать модель на тестовой выборке"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Получим прогнозы целевой переменной на тестовых данных для модели линейной регрессии. \n",
    "\n",
    "Для этого вызовем у каждой модели метод **predict()**, в качестве аргумента передадим *X_test*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_predictions_linear = linear_regression_model.predict(X_test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Качество регрессионной модели оценим двумя способами: \n",
    "1. Сравним визуально прогнозы с настоящими значениями (тестовые с предсказанием)\n",
    "2. Сравним метрики качества"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Визуализируем прогноз линейной модели и настоящие значения из тестовой выборки."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure(figsize=(7, 7))\n",
    "plt.scatter(y_test, test_predictions_linear) # рисуем точки, соответствущие парам настоящее значение - прогноз\n",
    "plt.plot([-5, 15], [-5, 15]) # рисуем прямую, на которой предсказания и настоящие значения совпадают\n",
    "plt.xlabel('Настоящее ln УЭС', fontsize=20)\n",
    "plt.ylabel('Предсказанное ln УЭС', fontsize=20);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Вычислим **метрики качества регрессионной модели**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Для корректного подсчета метрик качества модели в python требуется загрузить их из библиотеки **sklearn**. \n",
    "\n",
    "Мы используем три метрики качества:\n",
    " * *mean_absolute_error* - средняя абсолютная ошибка $MAE=\\sum\\limits_{i=1}^{n}\\frac{|y_i - \\hat{y}_i|}{n}$\n",
    " * *mean_squared_error* - средняя квадратичная ошибка $MSE=\\sum\\limits_{i=1}^{n}\\frac{(y_i - \\hat{y}_i)^2}{n}$\n",
    " * *r2_score* - коэффициент детерминации $R^2={\\frac{\\sum\\limits_{i=1}^{n}(\\hat{y}_i-\\bar{y})^2}{\\sum\\limits_{i=1}^{n}(y_i-\\bar{y})^2}}={1-\\frac{\\sum\\limits_{i=1}^{n}(y_i-\\hat{y}_i)^2}{\\sum\\limits_{i=1}^{n}(y_i-\\bar{y})^2}}$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Подсчитаем ошибки для линейной модели.\n",
    "\n",
    "Для этого вызовем методы **mean_absolute_error()**, **mean_squared_error()** и **r2_score()**. На вход им передается столбец настоящих значений *y_test* и столбец значений, предсказанных моделью линейной регрессии *test_predictions_linear*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mean_absolute_error_linear_model = mean_absolute_error(y_test, test_predictions_linear) \n",
    "mean_squared_error_linear_model = mean_squared_error(y_test, test_predictions_linear)\n",
    "r2_score_linear_model = r2_score(y_test, test_predictions_linear)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Теперь напечатаем полученные ошибки. Обычно смотрят на корень из среднеквадратичной ошибки, RMSE. Чтобы извлечь корень нам понадобится библиотека **Numpy**. С помощью неё можно быстро производить вычисления сразу над массивами чисел"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "# %7.2f - вывод числа с плавающей точкой (f - float/double); 7 - до скольки дополнять пробелами, .2 - сколько символов после запятой\n",
    "\n",
    "print(\"MAE: {0:7.2f}, RMSE: {1:7.2f}, R2: {2:7.2f} для линейной модели\".format(\n",
    "        mean_absolute_error_linear_model, \n",
    "        np.sqrt(mean_squared_error_linear_model),\n",
    "        r2_score_linear_model))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "colab": {
   "provenance": [],
   "toc_visible": true
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
